package org.firebug.spring.boot.redisson.service;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.client.RedisException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class RedissonService {
    @Autowired
    private RedissonClient redissonClient;

    private void doSomething(long sleepMillis) {
        System.out.println("D1: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000);
        try {
            Thread.sleep(sleepMillis);
        } catch (InterruptedException e) {
        }
        System.out.println("D2: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000);
    }

    /**
     * (同步)可重入锁
     *  attention: leaseMillis > sleepMillis
     */
    public void reentrantLock(String key, long leaseMillis, long sleepMillis) {
        RLock lock = redissonClient.getLock(key);
        // 1. lock fail -> blocking
        // 2. lock fail -> org.redisson.client.RedisException
//        lock.lock();
        lock.lock(leaseMillis, TimeUnit.MILLISECONDS);
        try {
            doSomething(sleepMillis);
        } finally {
            System.out.println("U1: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000);
            // leaseMillis < sleepMillis -> dead lock
            lock.unlock(); //unlock fail -> java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: e6cf2b17-bc0c-4bfb-9dd9-10afc7b201f4 thread-id: 1
            System.out.println("U2: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000);
        }
    }

    /**
     * (同步)可重入(尝试)锁
     */
    public void reentrantTryLock(String key, long waitMillis, long leaseMillis, long sleepMillis) throws InterruptedException {
        RLock lock = redissonClient.getLock(key);
//        boolean locked = lock.tryLock(); //try lock wait {waitMillis:0}, when timeout return false;
//        boolean locked = lock.tryLock(waitMillis, TimeUnit.MILLISECONDS);
        boolean locked = lock.tryLock(waitMillis, leaseMillis, TimeUnit.MILLISECONDS);
        if (locked) {
            try {
                doSomething(sleepMillis);
            } finally {
                lock.unlock(); //java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id
                System.out.println("U: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000);
            }
        }
    }

//
//    /**
//     * (异步)可重入锁
//     */
//    public void reentrantLockAsync(String key, long waitMillis, long leaseMillis, TimeUnit timeUnit) throws ExecutionException, InterruptedException {
//        RLock lock = redissonClient.getLock(key);
////        RFuture future = lock.lockAsync();
////        RFuture future = lock.lockAsync(leaseMillis, timeUnit);
//        RFuture future = lock.lockAsync(leaseMillis, timeUnit, Thread.currentThread().getId());
//        future.get(); //can not get lock then blocking
//        try {
//            doSomething();
//        } finally {
//            lock.unlock(); //java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id
//            System.out.println("U: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000);
//        }
//    }
//
//    /**
//     * (异步)可重入(尝试)锁
//     */
//    public void reentrantTryLockAsync(String key, long waitMillis, long leaseMillis, TimeUnit timeUnit) throws ExecutionException, InterruptedException {
//        RLock lock = redissonClient.getLock(key);
////        RFuture<Boolean> future = lock.tryLockAsync();
////        RFuture<Boolean> future = lock.tryLockAsync(Thread.currentThread().getId());
////        RFuture<Boolean> future = lock.tryLockAsync(waitMillis, timeUnit);
////        RFuture<Boolean> future = lock.tryLockAsync(waitMillis, leaseMillis, timeUnit);
//        RFuture<Boolean> future = lock.tryLockAsync(waitMillis, leaseMillis, timeUnit, Thread.currentThread().getId());
//        boolean locked = future.get(); //try lock wait {waitMillis}, when timeout return false;
//        if (locked) {
//            try {
//                doSomething();
//            } finally {
//                lock.unlock(); //java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id
//                System.out.println("U: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000);
//            }
//        }
//    }
//
//    /**
//     * 公平锁（可重入）
//     */
//    public void reentrantFairLock(String key, long waitMillis, long leaseMillis, TimeUnit timeUnit) {
//        RLock lock = redissonClient.getFairLock(key);// 公平锁
//        lock.lock(leaseMillis, timeUnit);
//        try {
//            doSomething();
//        } finally {
//            lock.unlock(); //java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id
//            System.out.println("U: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000);
//        }
//    }
//
//    /**
//     * 联锁
//     */
//    public void reentrantMultiLock(String key1, String key2, String key3, long waitMillis, long leaseMillis, TimeUnit timeUnit) {
//        RLock lock1 = redissonClient.getLock(key1);
//        RLock lock2 = redissonClient.getLock(key2);
//        RLock lock3 = redissonClient.getLock(key3);
//        RLock lock = redissonClient.getMultiLock(lock1, lock2, lock3);
//        lock.lock(leaseMillis, timeUnit);
//        try {
//            doSomething();
//        } finally {
//            lock.unlock(); //java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id
//            System.out.println("U: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000);
//        }
//    }
//
//    /**
//     * 红锁
//     */
//    public void reentrantRedLock(String key1, String key2, String key3, long waitMillis, long leaseMillis, TimeUnit timeUnit) {
//        RLock lock1 = redissonClient.getLock(key1);
//        RLock lock2 = redissonClient.getLock(key2);
//        RLock lock3 = redissonClient.getLock(key3);
//        System.out.println("S: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000);
//        RLock lock = redissonClient.getRedLock(lock1, lock2, lock3);
//        lock.lock(leaseMillis, timeUnit);
////        lock.lock();
//        try {
//            doSomething();
//        } finally {
//            lock.unlock(); //java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id
//            System.out.println("U: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000);
//        }
//    }
//
//    /**
//     * 读(写)锁: 多读一写
//     */
//    public void readLock(String key, long waitMillis, long leaseMillis, TimeUnit timeUnit) {
//        RLock lock = redissonClient.getReadWriteLock(key).readLock();
//        boolean locked = false;
//        try {
//            locked = lock.tryLock(waitMillis, leaseMillis, timeUnit);
//            if (locked) {
//                doSomething();
//            }
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        } finally {
//            if (locked) {
//                lock.unlock();
//                System.out.println("R U: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000);
//            }
//        }
//    }
//    /**
//     * (读)写锁: 多读一写
//     */
//    public void writeLock(String key, long waitMillis, long leaseMillis, TimeUnit timeUnit) {
//        RLock lock = redissonClient.getReadWriteLock(key).writeLock();
//        boolean locked = false;
//        try {
//            locked = lock.tryLock(waitMillis, leaseMillis, timeUnit);
//            if (locked) {
//                doSomething();
//            }
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        } finally {
//            if (locked) {
//                lock.unlock();
//                System.out.println("W U: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000);
//            }
//        }
//    }
//
//    /**
//     * 闭锁
//     */
//    public void countDownLatch(String key, long count) {
//        RCountDownLatch latch = redissonClient.getCountDownLatch(key);
//        latch.trySetCount(count);
//
//        for (int i = 0; i < count; i++) {
//            new Thread(() -> {
//                try {
//                    System.out.println("A: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000 + ", message=start");
//                    Thread.sleep((int) (Math.random() * 1000));
//                    System.out.println("B: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000 + ", message=sleep");
//                    latch.countDown(); // 非阻塞
//                    System.out.println("C: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000 + ", message=countDown");
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//            }).start();
//        }
//
//        try {
//            System.out.println("D: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000 + ", message=await before");
//            latch.await(); //阻塞
////            latch.await(3L, TimeUnit.SECONDS); //阻塞指定时间,不抛异常
//            System.out.println("E: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000 + ", message=await after");
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//    }
//
//    /**
//     * 信号量
//     */
//    public void semaphore(String key, int permits) {
//        RSemaphore semaphore = redissonClient.getSemaphore(key);
//        semaphore.trySetPermits(permits);
//        for (int i = 0; i < permits * 3; i++) {
//            new Thread(() -> {
//                try {
//                    System.out.println("A: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000 + ", message=start");
//                    semaphore.acquire(1); //semaphore.tryAcquire()
//                    System.out.println("B: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000 + ", message=acquire");
//                    Thread.sleep((int) (Math.random() * 1000));
//                    System.out.println("C: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000 + ", message=sleep");
//                    semaphore.release(1);
//                    System.out.println("D: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000 + ", message=release");
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//            }).start();
//        }
//    }
//
//    /**
//     * 可过期性信号量
//     */
//    public void permitExpirableSemaphore(String key, int permits) {
//        RPermitExpirableSemaphore semaphore = redissonClient.getPermitExpirableSemaphore(key);
//        semaphore.trySetPermits(permits);
//        for (int i = 0; i < permits; i++) {
//            new Thread(() -> {
//                try {
////                    String permitId = semaphore.acquire();
//                    String permitId = semaphore.acquire(1, TimeUnit.SECONDS); //获取一个信号，有效期只有1秒钟
//                    Thread.sleep((int) (Math.random() * 1000));
//                    System.out.println("A: thread=" + Thread.currentThread().getName() + ", time=" + System.currentTimeMillis() / 1000);
//                    semaphore.release(permitId);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//            }).start();
//        }
//    }

    public boolean send(String message) throws Exception{
        return true;
    }

    public boolean tryLockAndSend(String key, String message) throws Exception {
        RLock lock = redissonClient.getLock(key);
        boolean locked = lock.tryLock();
        if (locked) {
            try {
                return send(message);
            } finally {
                lock.unlock();
            }
        }
        throw new RuntimeException();
    }
}
